package svgedit.gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;

import svgedit.gui.actions.DeleteAction;
import svgedit.gui.actions.DocPropAction;
import svgedit.gui.actions.GroupAction;
import svgedit.gui.actions.InsertCircleAction;
import svgedit.gui.actions.InsertLineAction;
import svgedit.gui.actions.InsertRectAction;
import svgedit.gui.actions.NewAction;
import svgedit.gui.actions.OpenDocumentAction;
import svgedit.gui.actions.QuitAction;
import svgedit.gui.actions.SaveAction;
import svgedit.gui.actions.SaveAsAction;
import svgedit.gui.actions.SelectAllAction;
import svgedit.gui.actions.UnGroupAction;
import svgedit.svg.SVGDocument;
import svgedit.xml.SVGFileWriter;
import svgedit.xml.XMLReader;

/** Displays a window containing a SVG view, toolbar and menu. */
@SuppressWarnings("serial")
public class Frame extends JFrame
{

	/** Singleton Frame instance */
	private static Frame framePtr = null;

	/** gets Singleton Frame instance, creating a new one if necessary */
	public static Frame getFrame()
	{
		if (framePtr == null)
		{
			System.err.println("Frame not init!");
			System.exit(1);
		}
		return framePtr;
	}

	/**
	 * To ensure safe init of frame you must init a frame before getting it
	 * 
	 * @see Frame#getFrame()
	 * @return initialised Frame
	 */
	public static Frame initFrame()
	{
		if (framePtr != null)
		{
			System.err.println("Frame already init!");
			System.exit(1);
		}
		return (framePtr = new Frame());
	}

	/** User preferences */
	private Preferences preferences;

	/** The document displayed by the frame */
	private SVGDocument document;

	/** An SVG view of the current document */
	private View view;

	/** menutoolbar */
	private JPanel menuToolbar;

	/** define the xvg writer here **/
	private SVGFileWriter writer;

	// Actions available from the menu and toolbar */
	private Action newAction;
	private Action openAction;
	private Action quitAction;
	private Action saveAction;
	private Action saveAsAction;
	private Action docPropAction;

	private Action insertRectAction;
	private Action insertCircleAction;
	private Action insertLineAction;

	private Action editUnGroup;
	private Action editGroup;
	private Action editSelectAll;
	private Action editDelete;

	private JMenuItem mnuEditGroup;
	private JMenuItem mnuEditUnGroup;
	private JMenuItem mnuSelectAll;
	private JMenuItem mnuEditDelete;

	private boolean changesMade = false;

	/** A toolbar displayed by the frame */
	private Toolbar toolbar;

	/** Creates a frame with a new, empty document. */
	private Frame()
	{
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		this.addWindowListener(new QuitAction());
		menuToolbar = new JPanel(new BorderLayout());
		preferences = new Preferences();

		// Create all actions presented by the menu bar and toolbar
		newAction = new NewAction();
		openAction = new OpenDocumentAction();
		saveAction = new SaveAction();
		saveAsAction = new SaveAsAction();
		docPropAction = new DocPropAction();
		quitAction = new QuitAction();

		insertRectAction = new InsertRectAction();
		insertCircleAction = new InsertCircleAction();
		insertLineAction = new InsertLineAction();

		editGroup = new GroupAction();
		editUnGroup = new UnGroupAction();
		editSelectAll = new SelectAllAction();
		editDelete = new DeleteAction();

		mnuEditGroup = new JMenuItem(editGroup);
		mnuEditUnGroup = new JMenuItem(editUnGroup);
		mnuSelectAll = new JMenuItem(editSelectAll);
		mnuEditDelete = new JMenuItem(editDelete);
		mnuEditGroup.setEnabled(false);
		mnuEditUnGroup.setEnabled(false);
		mnuEditDelete.setEnabled(false);

		// Associate keyboard shortcuts with actions - note that these will use
		// mac ready shortcuts
		// crtl-o in windows automagically becomes command-o in mac.
		newAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));

		editSelectAll.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));

		openAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));
		saveAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));

		insertRectAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_R, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));
		insertCircleAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));
		insertLineAction.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_L, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));

		editGroup.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_G, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));
		editUnGroup.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_U, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));
		editDelete.putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke
				.getKeyStroke(KeyEvent.VK_DELETE, Toolkit.getDefaultToolkit()
						.getMenuShortcutKeyMask()));

		// Create menus - add to menuToolbar JPanel
		JMenuBar menuBar = new JMenuBar();
		menuToolbar.add(menuBar, BorderLayout.NORTH);

		// File menu
		JMenu fileMenu = new JMenu("File");
		fileMenu.add(new JMenuItem(newAction));
		fileMenu.add(new JMenuItem(openAction));
		fileMenu.add(new JSeparator());
		fileMenu.add(new JMenuItem(saveAction));
		fileMenu.add(new JMenuItem(saveAsAction));
		fileMenu.add(new JSeparator());
		fileMenu.add(new JMenuItem(docPropAction));
		fileMenu.add(new JSeparator());
		fileMenu.add(new JMenuItem(quitAction));
		menuBar.add(fileMenu);

		// Edit menu
		JMenu editMenu = new JMenu("Edit");
		editMenu.add(mnuSelectAll);
		editMenu.add(new JSeparator());
		editMenu.add(mnuEditGroup);
		editMenu.add(mnuEditUnGroup);
		editMenu.add(new JSeparator());
		editMenu.add(mnuEditDelete);
		menuBar.add(editMenu);

		// Insert menu
		JMenu insertMenu = new JMenu("Insert");
		insertMenu.add(new JMenuItem(insertRectAction));
		insertMenu.add(new JMenuItem(insertCircleAction));
		insertMenu.add(new JMenuItem(insertLineAction));
		menuBar.add(insertMenu);

		// Create toolbar - add to menuToolbar JPanel
		toolbar = new Toolbar();
		menuToolbar.add(toolbar, BorderLayout.SOUTH);

		// add menuToolbar to frame
		add(menuToolbar, BorderLayout.NORTH);

		// Create view
		view = new View(toolbar);
		getContentPane().add(view);

		newFile();

		// Set an initial view size in case the document size is based on the
		// view
		view.setSize(new Dimension(480, 360));

		// Size the view to the document
		view.setPreferredSize(new Dimension((int) document.getWidth()
				.getValue(), (int) document.getHeight().getValue()));
		pack();

		// make our writer object
		writer = new SVGFileWriter();
	}

	/**
	 * Gets the preferences object to use for getting and setting user defaults.
	 * 
	 * @return the preferences object associated with this frame
	 */
	public Preferences getPreferences()
	{
		return preferences;
	}

	/**
	 * Gets the document currently displayed in the frame.
	 * 
	 * @return an SVG document
	 */
	public SVGDocument getDocument()
	{
		return document;
	}

	/**
	 * Sets the document currently displayed in the frame.
	 * 
	 * @param document
	 *            the document to display
	 * @param file
	 *            the file the document was loaded from, or null if it's a new
	 *            file
	 */
	private void setDocument(SVGDocument document, File file)
	{
		this.document = document;
		view.setDocument(document);
		setFile(file);
	}

	/** Gets the view presenting the document for this frame. */
	public View getView()
	{
		return view;
	}

	/**
	 * Set the filename of the current document. Sets the frame's title and
	 * enables the appropriate save menu items.
	 * 
	 * @param file
	 *            the filename of the current document, or null if the document
	 *            is new and has no filename
	 */
	private void setFile(File file)
	{
		document.setFile(file);

		if (file != null)
		{
			setTitle(file.getName());
			this.preferences.setCurrentFile(file.getName());
			this.preferences.setDefaultPath(file.getPath());
		}
		else
		{
			setTitle("New File");
			this.preferences.setCurrentFile("New File");
			System.out.println("default path:"
					+ this.preferences.getDefaultPath());
		}
	}

	/** Sets the document to a new document with no associated file. */
	public final void newFile()
	{
		SVGDocument doc = new SVGDocument(view);
		setDocument(doc, null);
	}

	/**
	 * Loads a file and sets it as the current document. Displays an error
	 * message and leaves the current document unchanged if the document cannot
	 * be opened.
	 * 
	 * @param file
	 *            the file to load
	 */
	public final void openFile(File file)
	{
		try
		{
			SVGDocument doc = XMLReader.read(file, view);
			setDocument(doc, file);
		}
		catch (IOException e)
		{
			JOptionPane.showMessageDialog(getRootPane(), e.getMessage(),
					"Unable to open file", JOptionPane.ERROR_MESSAGE);
		}
	}

	public static final int DIALOG_MODE_FORCE = 1;
	public static final int DIALOG_MODE_CHECK = 0;

	/**
	 * Saves the file either as Save As or save the current document
	 */
	public int displaySaveDialog()
	{

		int opt = JOptionPane.showConfirmDialog(this, "Save Changes?",
				"Confirm", JOptionPane.YES_NO_CANCEL_OPTION);
		return opt;
	}

	public final boolean saveFile(int mode)
	{

		// will get the filename and path out of preferences.
		String fn = (this.preferences).getCurrentCurrentFile();
		String path = (this.preferences).getDefaultPath();
		File file;

		// Shows new file dialog
		if (fn.equals("New File") || mode == DIALOG_MODE_FORCE)
		{

			JFileChooser dialog = new JFileChooser(path);
			dialog.showSaveDialog(null);
			file = dialog.getSelectedFile();

			if (file == null)
				return false;

			// Check if file exists
			if (file.exists())
			{
				int opt = JOptionPane.showConfirmDialog(this, "Override File?",
						"confirm", JOptionPane.YES_NO_CANCEL_OPTION);
				if (opt == JOptionPane.NO_OPTION
						|| opt == JOptionPane.CANCEL_OPTION)
					return false;
			}

		}
		else
		{
			// create new file with this path/file
			file = new File(path);
		}

		// now write
		try
		{

			System.out.println("Saving to: \npath:" + file.getPath()
					+ "\nFilename: " + file.getName());

			writer.write(file, this.document);
			this.modifiedTitle(false);
			setFile(file);

		}
		catch (Exception e)
		{
			JOptionPane.showMessageDialog(getRootPane(), e.getMessage(),
					"Error saving file", JOptionPane.ERROR_MESSAGE);
			// e.printStackTrace();
			return false;
		}

		return false;
	}

	/**
	 * Gets the toolbar currently displayed by the frame
	 * 
	 * @return the toolbar
	 */
	public Toolbar getToolbar()
	{
		return toolbar;
	}

	/**
	 * Show the colour chooser dialog
	 * 
	 * @param colorPreviewBox
	 *            The button to give the colour to / visual
	 * @param opt
	 *            If it is stroke or fill that is being clicked
	 */
	public void drawColorChooser(JButton colorPreviewBox, int opt)
	{
		new ColorChooser(this, colorPreviewBox, opt);
	}

	/**
	 * Show/Hide the menu item for group
	 * 
	 * @param enable
	 *            Enabled?
	 */
	public void enableGroupMenuItem(boolean enable)
	{
		mnuEditGroup.setEnabled(enable);
	}

	/**
	 * Show/Hide the menu item for ungroup
	 * 
	 * @param enable
	 *            Enabled?
	 */
	public void enableUnGroupMenuItem(boolean enable)
	{
		mnuEditUnGroup.setEnabled(enable);

	}

	/**
	 * Show/Hide the menu item for delete
	 * 
	 * @param enable
	 *            Enabled?
	 */
	public void enableDeleteMenuItem(boolean enable)
	{
		mnuEditDelete.setEnabled(enable);
	}

	/**
	 * Have changed been made to document
	 * 
	 * @return True/False if any changes have been made
	 */
	public boolean getChangesMade()
	{
		return this.changesMade;
	}

	/**
	 * Show (modified) in title
	 * 
	 * @param toggle
	 *            Toggle it?
	 */
	public void modifiedTitle(boolean toggle)
	{
		this.changesMade = toggle;
		if (toggle)
		{
			this.setTitle((this.preferences).getCurrentCurrentFile()
					+ " (Modified)");
		}
		else
			this.setTitle((this.preferences).getCurrentCurrentFile());

	}
}
